package com.theorydance.content_search;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import com.theorydance.content_search.util.Condition;

/**
 * 可以进行文件内容搜索，也可以进行文件名搜索，可以进行指定关键字过滤<br>
 * <font color='red'>测试方式，见CommonSearchTest.java</font><br>
 * 在windows中路径为“\”, 在linux中路径为“/”
 */
public class CommonSearch {

	/**
		设置工作线程个数
	 */
	private int workerThread = 0;
	private ExecutorService workerThreadPool = null;
	/**
	 * 输出序号
	 */
	private AtomicInteger indexNum = new AtomicInteger();
	/**
	 * 总的扫描文件个数
	 */
	private AtomicInteger totalFileNum = new AtomicInteger();
	private BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
	/**
	 * 是否输出最终过滤结果
	 */
	private boolean outputResult;
	/**
	 * 是否过程
	 */
	private boolean outputProcess;

	private CommonSearch(boolean outputResult, boolean outputProcess) {
		// 默认工作线程为电脑的CPU数量*2，获取的是线程数
		workerThread = Runtime.getRuntime().availableProcessors();
		this.outputResult = outputResult;
		this.outputProcess = outputProcess;
	}

	private CommonSearch init(){
		workerThreadPool = new ThreadPoolExecutor(this.workerThread, this.workerThread,
				30L,TimeUnit.SECONDS,
				taskQueue);
		return this;
	}

	/**
	 * 获取CommonSearch实例
	 * @return
	 */
	public static CommonSearch getInstance() {
		return new CommonSearch(true, true);
	}
	public static CommonSearch getInstance(boolean outputResult, boolean outputProcess) {
		return new CommonSearch(outputResult, outputProcess);
	}

	/**
	 * 设置工作线程个数
	 * @param workerThread 有效线程数为正数，设置为0或负数则采用默认线程数
	 * @return
	 */
	public CommonSearch setWorkerThread(int workerThread) {
		if (workerThread > 0) {
			this.workerThread = workerThread;
		}
		return this;
	}

	/**
	 * 进行单次搜索
	 * @param scope 匹配的文件条件
	 * @param dirs 需要匹配的文本内容，至少传递一个目录
	 */
	public CommonSearch search(Function<File, Condition> scope, String... dirs) {

		if (dirs.length == 0) {
			return this;
		}
		// 初始化worker线程池
		init();

		// 设置遍历过的文件集合
		Set<String> completedFilenameSet = new HashSet<>();
		for (String dirStr : dirs) {
			System.out.println("遍历目录：" + dirStr);
			try {
				Files.walkFileTree(Paths.get(dirStr), new SimpleFileVisitor<Path>() {
					/*
					 优化思路（针对整个磁盘搜索速度慢）：
					  	1. 可以在此处将需要判断并进行处理的内容，放入到一个队列中，该线程只入队列不处理，然后多线程进行处理判断；（OK）
					  	TODO 2. 部分能够提前判断，可以不用进入子树，进了也是白进；有点不够智能
					 */
					@Override
					public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
						// 总文件个数进行自增
						if( totalFileNum.incrementAndGet() % 1000 == 0 && outputProcess) {
							System.out.println("扫描文件个数：" + totalFileNum.get() + "，线程池中的任务队列中排队个数：" + CommonSearch.this.taskQueue.size());
						}

						// 将需要判断的逻辑放入到worker线程池中进行判断
						workerThreadPool.submit(() -> {
							// 判断当前文件名是否满足正则条件
							Condition condition = scope.apply(file.toFile());
							if(condition.apply()) {
								String fileAbsolutePath = file.toFile().getAbsolutePath();
								if (completedFilenameSet.add(fileAbsolutePath) && outputResult) {
									System.out.println(indexNum.incrementAndGet() + " " + file.toString() + " 线程：" + Thread.currentThread());
								}
							}
						});
						return FileVisitResult.CONTINUE;
					}
					@Override
					public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
						return FileVisitResult.CONTINUE;
					}
				});
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		workerThreadPool.shutdown();
		// 等待执行完毕
		try{
			workerThreadPool.awaitTermination(5, TimeUnit.MINUTES);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}

	public CommonSearch await() {
		try {
			workerThreadPool.awaitTermination(1, TimeUnit.DAYS);
		}catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return this;
	}
	
	
}
